use anyhow::Result;
use colored::*;
use serde_json::Value;
use std::fs;

pub async fn run(input: String, interface_name: String, output: Option<String>) -> Result<()> {
    println!("{} {} to TypeScript", "🔄 Converting".cyan(), input);

    let content = fs::read_to_string(&input)?;
    let json: Value = serde_json::from_str(&content)?;

    let typescript = json_to_typescript(&json, &interface_name, 0);

    if let Some(output_path) = output {
        fs::write(&output_path, &typescript)?;
        println!("{} {}", "✅ Generated".green(), output_path);
    } else {
        println!("{}", typescript);
    }

    Ok(())
}

fn json_to_typescript(value: &Value, name: &str, indent: usize) -> String {
    let indent_str = "  ".repeat(indent);

    match value {
        Value::Object(map) => {
            let mut result = format!("{}export interface {} {{\n", indent_str, name);

            for (key, val) in map {
                let ts_type = match val {
                    Value::String(_) => "string".to_string(),
                    Value::Number(_) => "number".to_string(),
                    Value::Bool(_) => "boolean".to_string(),
                    Value::Array(arr) => {
                        if let Some(first) = arr.first() {
                            format!("{}[]", get_type_name(first))
                        } else {
                            "any[]".to_string()
                        }
                    }
                    Value::Object(_) => format!("{}Item", name),
                    Value::Null => "null".to_string(),
                };

                result.push_str(&format!("{}  {}: {};\n", indent_str, key, ts_type));
            }

            result.push_str(&format!("{}}}\n", indent_str));
            result
        }
        _ => "// Unable to convert non-object JSON to interface\n".to_string(),
    }
}

fn get_type_name(value: &Value) -> String {
    match value {
        Value::String(_) => "string".to_string(),
        Value::Number(_) => "number".to_string(),
        Value::Bool(_) => "boolean".to_string(),
        Value::Object(_) => "object".to_string(),
        Value::Array(_) => "any[]".to_string(),
        Value::Null => "null".to_string(),
    }
}
